using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using Sherwood.Imaging.Providers;

namespace Sherwood.Imaging.Filters
{
    public class ScaleImageFilter : IFilter
    {
        public ScaleImageFilter(int maxWidth, int maxHeight, ResizeBehavior resizeBehavior) 
            : this(maxWidth, new StaticNumberProvider(maxHeight), resizeBehavior)
        {
        }

        public ScaleImageFilter(int maxWidth, INumberProvider maxHeight, ResizeBehavior resizeBehavior) 
            : this(new StaticNumberProvider(maxWidth), maxHeight, resizeBehavior)
        {
        }

        public ScaleImageFilter(INumberProvider maxWidth, int maxHeight, ResizeBehavior resizeBehavior) 
            : this(maxWidth, new StaticNumberProvider(maxHeight), resizeBehavior)
        {
        }

        public ScaleImageFilter(INumberProvider maxWidth, INumberProvider maxHeight, ResizeBehavior resizeBehavior)
        {
            MaxWidth = maxWidth;
            MaxHeight = maxHeight;
            ResizeBehavior = resizeBehavior;
        }

        public INumberProvider MaxWidth { get; set; }
        public INumberProvider MaxHeight { get; set; }
        public ResizeBehavior ResizeBehavior { get; set; }

        /// <exception cref="ArgumentNullException"><c>original</c> is null.</exception>
        public Image ApplyFilter(Image original)
        {
            if (original == null) throw new ArgumentNullException("original");

            Size newSize;
            Rectangle sourceRectangle;
            var maxWidth = MaxWidth == null ? 0 : MaxWidth.GetNumber();
            var maxHeight = MaxHeight == null ? 0 : MaxHeight.GetNumber();
            ResizeBehavior.DoResize(original.Size,
                                    maxWidth == 0 ? new int?() : (int) maxWidth,
                                    maxHeight == 0 ? new int?() : (int) maxHeight,
                                    out newSize, out sourceRectangle);

            var image = new Bitmap(newSize.Width, newSize.Height, original.PixelFormat);
            var rect = new Rectangle(0, 0, image.Width, image.Height);

            using (Graphics graphics = Graphics.FromImage(image))
            {
                // to avoid a black border around the picture, we first draw the picture a bit larger, 
                // so that we have a pretty well matching background even before we start reducing the size
                var tmpRect = rect;
                tmpRect.Inflate(2, 2);
                tmpRect.Offset(-1, -1);
                graphics.CompositingQuality = CompositingQuality.HighSpeed;
                graphics.SmoothingMode = SmoothingMode.HighSpeed;
                graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
                graphics.DrawImage(original, tmpRect, sourceRectangle, GraphicsUnit.Pixel);

                graphics.CompositingQuality = CompositingQuality.HighQuality;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.DrawImage(original, rect, sourceRectangle, GraphicsUnit.Pixel);

                return image;
            }
        }
    }
}